function literal 由於沒有 function 名稱所以又可稱為匿名函式(anonymous function),又有另外一個名稱 lambda expressions.之前有提到 val 是在定義變數,但這邊因為匿名函式(anonymous function) 的關係,所以可以把它綁定到一個變數上 :
scala> val sum = (num1: Int,num2: Int) => num1 + num2
sum: (Int, Int) => Int = $$Lambda$1182/1936666552@9715d26
scala> sum(1,2)
res10: Int = 3
使用 underscore (_) 簡化 lambda expressions :
scala> val sum = (_:Int) + (_:Int)
sum: (Int, Int) => Int = $$Lambda$1189/26179918@389a9ff6
scala> sum(1,2)
res11: Int = 3
或著這樣寫
scala> val sum : (Int , Int) => Int = _ + _
sum: (Int, Int) => Int = $$Lambda$1267/1294798554@2ce987d7
再來看下面例子,如果參數只有一個的話,省略到最後會變成只要一個 _ ,且不用指定型態就可代表傳入的參數 :
scala> val sayHello = (name:String) => "Hello " + name
sayHello: String => String = $$Lambda$1195/2104281815@4d9ad37e
scala> val sayHello = "Hello " + (_:String)
sayHello: String => String = $$Lambda$1196/952756535@26401eda
scala> val sayHello = "Hello " + _
sayHello: Any => String = $$Lambda$1204/1132118748@3de507af
scala> sayHello("Daniel")
res12: String = Hello Daniel
function literal 運用在迴圈上 :
scala> 1.to(5).foreach((num:Int) => print(num + " "))
1 2 3 4 5
scala> (1 to 5).foreach(print)
12345
lambda 函數的運用,function countResult 定義了一個參數 counter 是 (Int,Int) => Int 型態的:
scala> val plus = (_:Int) + (_:Int)
plus: (Int, Int) => Int = $$Lambda$1138/1752601189@5e9355a6
scala> val times = (_:Int) * (_:Int)
times: (Int, Int) => Int = $$Lambda$1139/584593384@2175111e
scala> val minus = (_:Int) - (_:Int)
minus: (Int, Int) => Int = $$Lambda$1140/1250176650@6b909973
scala> val divided = (_:Int) / (_:Int)
divided: (Int, Int) => Int = $$Lambda$1141/976011906@62910d16
scala> def countResult(num1: Int , num2: Int ,counter: (Int,Int) => Int) = {
| counter(num1,num2)
| }
countResult: (num1: Int, num2: Int, counter: (Int, Int) => Int)Int
scala> countResult(2,3,plus)
res10: Int = 5
scala> countResult(2,3,times)
res11: Int = 6
scala> countResult(2,3,minus)
res12: Int = -1
scala> countResult(2,3,divided)
res13: Int = 0
上面的例子其實把 plus , times , minus , divided 改成用 def 宣告其實也可以 :
scala> def plus = (_:Int) + (_:Int)
plus: (Int, Int) => Int
scala> def countResult(num1: Int , num2: Int ,counter: (Int,Int) => Int) = {
| counter(num1,num2)
| }
countResult: (num1: Int, num2: Int, counter: (Int, Int) => Int)Int
scala> countResult(2,3,plus)
res14: Int = 5
那這樣lambda expressions 跟用 def 的差異在哪呢 ? 下面例子可以很明顯看出差異 :
定義一個 val 及 def 的 function,會隨機印出一個亂數
scala> val printMsg1 = scala.util.Random.nextInt
printMsg1: Int = 693378554
scala> def printMsg2 = scala.util.Random.nextInt
printMsg2: Int
會發現 printMsg1 印出的亂數都會一樣,代表說定義好就只會 new 一次 function :
scala> printMsg1
res1: Int = 693378554
scala> printMsg1
res2: Int = 693378554
printMsg2 則每次呼叫都會產生一個新的亂數,所以每次呼叫都會 new 一次 function :
scala> printMsg2
res4: Int = -325259645
scala> printMsg2
res5: Int = 1808372623
一開始看到這符號也真的是滿頭問號了,scala 的符號真的很多種.
??? 的意思就是可以先定義一個尚未實作的方法,就是已確定會有這個方法,但還不知道這方法的細節時可以先使用 ??? 先定義該方法,但尚未實作,這時候如果呼叫他會丟出一個NotImplementedError 的錯誤訊息 :
scala> val printMsg1:Int = ???
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284)
... 28 elided
scala> def printMsg2:Int = ???
printMsg2: Int
這邊稍微額外補充一下上面有提到使用 val 及 def 的差異,其實另外還有一種寫法是使用 lazy val 宣告.像上面的 printMsg1 會出現錯誤是因為,前面有提到使用 val 定義好就只會 new 一次 function,所以它在定義時就會產生了.那如果前面加的 lazy 代表說等真的使用到時再來產生 function ,所以下面的 printMsg3 compiler 會成功 :
scala> lazy val printMsg3:Int = ???
printMsg3: Int = <lazy>
scala> printMsg2
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284)
at .printMsg2(<console>:11)
... 28 elided
假設你需要 extends 一個抽象類別,但裡面需要實作抽象方法,但也許你還沒拿到規格,還不確定裡面的細節該如何實作,這時候可以先用 ???.而且別人如果不小心呼叫到,會有 NotImplementedError 這訊息提示對方該方法尚未實作完成.
scala> abstract class Counter {
| def count : Int
| }
defined class Counter
scala> class MyCounter extends Counter
<console>:12: error: class MyCounter needs to be abstract, since method count in class Counter of type => Int is not defined
class MyCounter extends Counter
^
scala> class MyCounter extends Counter {
| def count = ???
| }
defined class MyCounter